from flask import Flask, render_template, request, redirect, url_for, flash, session, send_from_directory, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from datetime import datetime, timedelta
import os
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import secure_filename
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed
from wtforms import StringField, PasswordField, SubmitField, TextAreaField
from wtforms.validators import DataRequired, Length, EqualTo
import markdown
import bleach
from bleach.sanitizer import ALLOWED_TAGS, ALLOWED_ATTRIBUTES
from flask_session import Session
import uuid
import shutil
from sqlalchemy import inspect
import platform
import psutil
import argparse
import collections
import random
import time
import logging

# 终端颜色
class TermColor:
    HEADER = '\033[95m'    # 紫色
    BLUE = '\033[94m'      # 蓝色
    GREEN = '\033[92m'     # 绿色
    YELLOW = '\033[93m'    # 黄色
    RED = '\033[91m'       # 红色
    CYAN = '\033[96m'      # 青色
    ENDC = '\033[0m'       # 重置
    BOLD = '\033[1m'       # 加粗
    UNDERLINE = '\033[4m'  # 下划线

try:
    import geoip2.database
    import geoip2.errors
    GEOIP_AVAILABLE = True
except ImportError:
    GEOIP_AVAILABLE = False

# IP地理位置查询函数
def get_ip_location(ip, reader=None):
    """根据IP地址获取地理位置信息"""
    if not GEOIP_AVAILABLE:
        return "未知位置 (GeoIP库未安装)"
        
    if ip == "127.0.0.1" or ip.startswith("192.168.") or ip.startswith("10.") or ip.startswith("172."):
        return "本地网络"
        
    try:
        if reader is None:
            return "未知位置 (GeoIP数据库未加载)"
            
        response = reader.city(ip)
        country = response.country.names.get('zh-CN', response.country.name)
        city = response.city.names.get('zh-CN', response.city.name)
        
        if country and city:
            return f"{country} - {city}"
        elif country:
            return country
        else:
            return "未知位置"
    except geoip2.errors.AddressNotFoundError:
        return "未知位置"
    except Exception as e:
        return f"查询错误: {str(e)}"

# 全局变量，用于存储访问统计信息
visitor_stats = {
    'total_requests': 0,
    'unique_ips': set(),
    'path_counts': collections.Counter(),
    'recent_visitors': collections.deque(maxlen=50),  # 最近50个访问者
    'geoip_reader': None,  # GeoIP数据库读取器
    'today_visits': 0,  # 今日访问量
    'today_date': datetime.now().strftime('%Y-%m-%d'),  # 当前日期
    'active_sessions': set(),  # 存储活跃会话ID
    'last_activity': {},  # 记录最后活动时间
    'online_users': 0,  # 当前在线用户数量
    'ip_access_times': {},  # 记录IP访问时间
    'banned_ips': {}  # 记录被封禁的IP及其解封时间
}

# 设置活跃会话超时时间（分钟）
ACTIVE_SESSION_TIMEOUT = 5

# 设置访问频率限制
MAX_REQUESTS_PER_MINUTE = 70  # 每分钟最大请求数
BAN_DURATION = 600  # 封禁时长（秒）

# 获取中国时区的当前时间（UTC+8）
def local_time():
    return datetime.utcnow() + timedelta(hours=8)

# 创建数据目录（如果不存在）
if not os.path.exists('instance'):
    os.makedirs('instance')

# 创建会话目录
if not os.path.exists('flask_session'):
    os.makedirs('flask_session')

# 创建图片上传目录
UPLOAD_FOLDER = os.path.join('static', 'uploads')
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

# 初始化 Flask 应用
app = Flask(__name__)
# 使用固定的密钥，避免每次重启会话失效
app.config['SECRET_KEY'] = 'your-secret-key-keep-it-secret'
# 使用绝对路径指定数据库位置
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{os.path.join(os.path.abspath(os.path.dirname(__file__)), "instance", "blog.db")}'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 设置会话配置
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_FILE_DIR'] = 'flask_session'  # 会话文件保存目录
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31)  # 会话保持31天
app.config['SESSION_USE_SIGNER'] = True  # 签名会话 cookie
# 设置上传文件夹和最大文件大小
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB 最大上传大小

# 初始化 Flask-Session
Session(app)

# 初始化数据库
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

# 允许的HTML标签和属性
ALLOWED_TAGS.extend(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'br', 'hr', 'pre', 'code', 'blockquote', 'table', 'thead', 'tbody', 'tr', 'th', 'td'])
ALLOWED_ATTRIBUTES.update({
    'a': ['href', 'title', 'target'],
    'img': ['src', 'alt', 'title', 'width', 'height'],
    'code': ['class'],
    'pre': ['class'],
})

# 表单类
class LoginForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired(), Length(min=2, max=80)])
    password = PasswordField('密码', validators=[DataRequired()])
    submit = SubmitField('登录')

class RegisterForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired(), Length(min=2, max=80)])
    password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
    password2 = PasswordField('确认密码', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('注册')

class ChangePasswordForm(FlaskForm):
    old_password = PasswordField('旧密码', validators=[DataRequired()])
    new_password = PasswordField('新密码', validators=[DataRequired(), Length(min=6)])
    new_password2 = PasswordField('确认新密码', validators=[DataRequired(), EqualTo('new_password')])
    submit = SubmitField('修改密码')

# 添加写作表单类
class PostForm(FlaskForm):
    title = StringField('标题', validators=[DataRequired(), Length(min=1, max=200)])
    content = TextAreaField('内容', validators=[DataRequired()])
    tags = StringField('标签')
    cover_image = FileField('封面图片（不超过500KB）', validators=[FileAllowed(['jpg', 'jpeg', 'png', 'gif'], '只允许上传图片文件！')])
    submit = SubmitField('发布')

# 评论表单类
class CommentForm(FlaskForm):
    content = TextAreaField('评论内容', validators=[DataRequired(), Length(min=1, max=1000)])
    submit = SubmitField('发表评论')

# 数据模型
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)
    registered_at = db.Column(db.DateTime, nullable=False, default=local_time)
    posts = db.relationship('Post', backref='author', lazy=True)
    ratings = db.relationship('Rating', backref='user', lazy=True, cascade="all, delete-orphan")
    login_history = db.relationship('LoginHistory', backref='user', lazy=True, cascade="all, delete-orphan")
    tasks = db.relationship('Task', backref='user', lazy=True, cascade="all, delete-orphan")

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, nullable=False, default=local_time)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    tags = db.relationship('Tag', secondary='post_tags', backref='posts')
    cover_image = db.Column(db.String(255), nullable=True)  # 封面图片路径
    rating_count = db.Column(db.Integer, default=0)         # 评分次数
    average_rating = db.Column(db.Float, default=0.0)       # 平均评分
    ratings = db.relationship('Rating', backref='post', lazy=True, cascade="all, delete-orphan")

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), unique=True, nullable=False)

# 添加用户的待办事项表
class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    description = db.Column(db.Text, nullable=True)
    created_at = db.Column(db.DateTime, nullable=False, default=local_time)
    completed = db.Column(db.Boolean, default=False)
    deadline = db.Column(db.DateTime, nullable=True)
    priority = db.Column(db.Integer, default=0)  # 0-低 1-中 2-高
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

# 添加随手记模型
class Note(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text, nullable=False)      # 记录内容
    location = db.Column(db.String(100), nullable=True)  # 地点
    created_at = db.Column(db.DateTime, nullable=False, default=local_time)  # 创建时间
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)  # 用户ID
    user = db.relationship('User', backref=db.backref('notes', lazy=True, cascade="all, delete-orphan"))

# 多对多关系表
post_tags = db.Table('post_tags',
    db.Column('post_id', db.Integer, db.ForeignKey('post.id'), primary_key=True),
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True)
)

class Rating(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
    value = db.Column(db.Integer, nullable=False)  # 评分值，1-5
    created_at = db.Column(db.DateTime, nullable=False, default=local_time)
    
    # 设置唯一约束，确保每个用户对每篇文章只能评分一次
    __table_args__ = (db.UniqueConstraint('user_id', 'post_id', name='unique_user_post_rating'),)

# 评论数据模型
class Comment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, nullable=False, default=local_time)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
    parent_id = db.Column(db.Integer, db.ForeignKey('comment.id'), nullable=True)  # 回复的父评论ID
    
    # 外键关系
    user = db.relationship('User', backref=db.backref('comments', lazy=True, cascade="all, delete-orphan"))
    post = db.relationship('Post', backref=db.backref('comments', lazy=True, cascade="all, delete-orphan"))
    replies = db.relationship('Comment', backref=db.backref('parent', remote_side=[id]), lazy=True)
    
    def is_reply(self):
        """判断是否为回复评论"""
        return self.parent_id is not None

# 访问统计数据模型
class Statistic(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    key = db.Column(db.String(50), unique=True, nullable=False)  # 统计项名称，如 'total_visits'
    value = db.Column(db.String(1000), nullable=False)  # 统计值，存为字符串，可以转换为其他类型
    updated_at = db.Column(db.DateTime, nullable=False, default=local_time, onupdate=local_time)
    
    @staticmethod
    def get_value(key, default=None):
        """获取指定键的统计值"""
        stat = Statistic.query.filter_by(key=key).first()
        if stat:
            return stat.value
        return default
    
    @staticmethod
    def set_value(key, value):
        """设置指定键的统计值"""
        stat = Statistic.query.filter_by(key=key).first()
        if stat:
            stat.value = str(value)
        else:
            stat = Statistic(key=key, value=str(value))
            db.session.add(stat)
        db.session.commit()

class LoginHistory(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    ip_address = db.Column(db.String(50), nullable=False)
    login_time = db.Column(db.DateTime, nullable=False, default=local_time)
    location = db.Column(db.String(100), nullable=True)
    
    # 直接使用 user_id 外键关联，无需重复定义关系
    # user = db.relationship('User', backref=db.backref('login_history', lazy=True, cascade="all, delete-orphan"))

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

# 自定义过滤器 - 将用户ID转换为注册日期
@app.template_filter('formatdate')
def format_date(user_id):
    user = User.query.get(user_id)
    if user and user.registered_at:
        return user.registered_at.strftime('%Y-%m-%d')
    return '未知'

# 路由
@app.route('/')
def index():
    # 获取所有文章，按创建时间倒序排列
    posts = Post.query.order_by(Post.created_at.desc()).all()
    
    # 为每篇文章处理纯文本摘要
    for post in posts:
        # 先将 markdown 转为 HTML
        html_content = markdown.markdown(post.content)
        # 将 HTML 转为纯文本（去除标签）
        text_content = bleach.clean(html_content, tags=[], strip=True)
        # 存储纯文本摘要
        post.text_excerpt = text_content[:150] + "..." if len(text_content) > 150 else text_content
    
    # 获取所有标签
    tags = Tag.query.all()
    
    # 更新在线用户数（简单模拟，实际情况需要更复杂的实现）
    update_active_users()
    
    # 获取访问统计数据
    today_visits = visitor_stats.get('today_visits', 0)
    total_visits = visitor_stats.get('total_requests', 0)
    
    return render_template('index.html', posts=posts, search_query=None, 
                          today_visits=today_visits, total_visits=total_visits, tags=tags)

@app.route('/about')
def about():
    return render_template('about.html')

@app.route('/profile')
@login_required
def profile():
    # 获取用户的待办事项
    tasks = Task.query.filter_by(user_id=current_user.id).order_by(Task.priority.desc(), Task.created_at.desc()).all()
    
    # 获取今天的日期（中国时区）
    today = datetime.now() + timedelta(hours=8)
    today = today.replace(hour=0, minute=0, second=0, microsecond=0)
    
    # 获取今日随手记
    today_notes = Note.query.filter(
        Note.user_id == current_user.id,
        Note.created_at >= today
    ).order_by(Note.created_at.desc()).all()
    
    # 计算三天前的日期
    three_days_ago = today - timedelta(days=3)
    
    # 获取近三天的待办事项和随手记（不包括今天）
    recent_tasks = Task.query.filter(
        Task.user_id == current_user.id,
        Task.created_at >= three_days_ago,
        Task.created_at < today
    ).order_by(Task.created_at.desc()).all()
    
    recent_notes = Note.query.filter(
        Note.user_id == current_user.id,
        Note.created_at >= three_days_ago,
        Note.created_at < today
    ).order_by(Note.created_at.desc()).all()
    
    return render_template('profile.html', 
                          tasks=tasks, 
                          today=today.date(), 
                          today_notes=today_notes,
                          recent_tasks=recent_tasks,
                          recent_notes=recent_notes,
                          three_days_ago=three_days_ago.date(),
                          timedelta=timedelta)

@app.route('/post/<int:post_id>')
def post(post_id):
    post = Post.query.get_or_404(post_id)
    # 将 Markdown 转换为 HTML
    content_html = markdown.markdown(post.content, 
                                     extensions=['fenced_code', 'tables', 'nl2br', 'codehilite'])
    
    # 获取当前用户的评分（如果已登录）
    user_rating = None
    if current_user.is_authenticated:
        rating = Rating.query.filter_by(user_id=current_user.id, post_id=post_id).first()
        if rating:
            user_rating = rating.value
    
    # 获取文章评论，按时间降序排列，不包括回复评论
    comments = Comment.query.filter_by(post_id=post_id, parent_id=None).order_by(Comment.created_at.desc()).all()
    
    # 评论表单
    form = CommentForm()
    
    # 如果用户已登录，显示评论表单
    comment_enabled = current_user.is_authenticated
    
    return render_template('post.html', post=post, content_html=content_html, 
                           user_rating=user_rating, comments=comments, form=form, 
                           comment_enabled=comment_enabled)

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user and user.check_password(form.password.data):
            # 设置会话为永久性
            session.permanent = True
            # 记住用户30天
            login_user(user, remember=True, duration=timedelta(days=30))
            
            # 记录登录历史
            ip_address = request.remote_addr
            location = get_ip_location(ip_address, visitor_stats['geoip_reader'])
            login_record = LoginHistory(
                user_id=user.id,
                ip_address=ip_address,
                location=location
            )
            db.session.add(login_record)
            db.session.commit()
            
            flash('登录成功！', 'success')
            return redirect(url_for('index'))
        flash('用户名或密码错误！', 'error')
    return render_template('login.html', form=form)

@app.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = RegisterForm()
    if form.validate_on_submit():
        if User.query.filter_by(username=form.username.data).first():
            flash('用户名已存在', 'error')
            return render_template('register.html', form=form)
        user = User(username=form.username.data)
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.commit()
        flash('注册成功！请登录', 'success')
        return redirect(url_for('login'))
    return render_template('register.html', form=form)

@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash('您已成功退出登录', 'success')
    return redirect(url_for('index'))

@app.route('/change_password', methods=['GET', 'POST'])
@login_required
def change_password():
    form = ChangePasswordForm()
    if form.validate_on_submit():
        user = current_user
        if not user.check_password(form.old_password.data):
            flash('旧密码不正确', 'error')
            return render_template('change_password.html', form=form)
        
        user.set_password(form.new_password.data)
        db.session.commit()
        flash('密码修改成功，请重新登录', 'success')
        logout_user()
        return redirect(url_for('login'))
    
    return render_template('change_password.html', form=form)

@app.route('/password_reset', methods=['GET'])
def password_reset():
    if current_user.is_authenticated:
        return redirect(url_for('change_password'))
    else:
        flash('请先登录后再修改密码', 'info')
        return redirect(url_for('login'))

# 处理图片上传
def save_image(file):
    if not file:
        return None
    
    # 检查图片大小限制 (500KB = 512000 字节)
    if len(file.read()) > 512000:
        # 重置文件指针位置
        file.seek(0)
        # 返回None表示图片过大，由调用方处理错误
        return None
    
    # 重置文件指针位置，确保可以正常保存
    file.seek(0)
    
    # 生成随机文件名，以避免文件名冲突
    filename = secure_filename(file.filename)
    random_name = f"{uuid.uuid4().hex}_{filename}"
    file_path = os.path.join(app.config['UPLOAD_FOLDER'], random_name)
    
    # 保存文件
    file.save(file_path)
    
    # 返回相对路径，使用正斜杠（/）避免Windows反斜杠（\）问题
    return os.path.join('uploads', random_name).replace('\\', '/')

@app.route('/write', methods=['GET', 'POST'])
@login_required
def write_post():
    form = PostForm()
    if form.validate_on_submit():
        # 处理封面图片上传
        cover_image_path = None
        if form.cover_image.data:
            cover_image_path = save_image(form.cover_image.data)
            # 检查图片大小是否超过限制
            if cover_image_path is None and form.cover_image.data:
                flash('图片大小超过500KB限制，请压缩后重新上传', 'error')
                return render_template('write_post.html', form=form)
        
        # 过滤 Markdown 内容
        safe_content = bleach.clean(form.content.data, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES, strip=True)
        
        # 创建新文章
        post = Post(
            title=form.title.data,
            content=safe_content,
            author=current_user,
            cover_image=cover_image_path,
            rating_count=0,
            average_rating=0.0
        )
        
        # 处理标签
        if form.tags.data:
            tag_names = [t.strip() for t in form.tags.data.split(',')]
            for tag_name in tag_names:
                if tag_name:
                    # 查找或创建标签
                    tag = Tag.query.filter_by(name=tag_name).first()
                    if not tag:
                        tag = Tag(name=tag_name)
                        db.session.add(tag)
                    post.tags.append(tag)
        
        db.session.add(post)
        db.session.commit()
        flash('文章发布成功！', 'success')
        
        # 设置会话变量，标记需要清除草稿
        session['clear_draft'] = True
        
        return redirect(url_for('post', post_id=post.id))
    
    # 设置清除草稿的 JavaScript 代码
    clear_draft_js = ""
    if session.pop('clear_draft', False):
        clear_draft_js = """
        localStorage.removeItem('draft_title');
        localStorage.removeItem('draft_content');
        localStorage.removeItem('draft_tags');
        console.log("草稿已清除");
        """
    
    return render_template('write_post.html', form=form, clear_draft_js=clear_draft_js)

@app.route('/edit_post/<int:post_id>', methods=['GET', 'POST'])
@login_required
def edit_post(post_id):
    post = Post.query.get_or_404(post_id)
    
    # 检查当前用户是否是文章作者
    if post.author != current_user:
        flash('您没有权限编辑这篇文章', 'error')
        return redirect(url_for('index'))
    
    form = PostForm()
    
    if form.validate_on_submit():
        # 处理封面图片上传
        if form.cover_image.data:
            # 保存新图片
            new_cover_image = save_image(form.cover_image.data)
            
            # 检查图片大小是否超过限制
            if new_cover_image is None:
                flash('图片大小超过500KB限制，请压缩后重新上传', 'error')
                return render_template('edit_post.html', form=form, post=post)
                
            # 如果有新图片上传，先删除旧图片（如果存在）
            if post.cover_image:
                old_image_path = os.path.join('static', post.cover_image)
                if os.path.exists(old_image_path):
                    try:
                        os.remove(old_image_path)
                    except:
                        pass  # 忽略删除错误
            
            # 更新封面图片路径
            post.cover_image = new_cover_image
        
        # 过滤 Markdown 内容
        safe_content = bleach.clean(form.content.data, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES, strip=True)
        
        # 更新文章内容
        post.title = form.title.data
        post.content = safe_content
        
        # 清除原有标签
        post.tags.clear()
        
        # 处理新标签
        if form.tags.data:
            tag_names = [t.strip() for t in form.tags.data.split(',')]
            for tag_name in tag_names:
                if tag_name:
                    # 查找或创建标签
                    tag = Tag.query.filter_by(name=tag_name).first()
                    if not tag:
                        tag = Tag(name=tag_name)
                        db.session.add(tag)
                    post.tags.append(tag)
        
        db.session.commit()
        flash('文章更新成功！', 'success')
        return redirect(url_for('post', post_id=post.id))
    
    # GET 请求时，填充表单数据
    if request.method == 'GET':
        form.title.data = post.title
        form.content.data = post.content
        form.tags.data = ','.join([tag.name for tag in post.tags])
    
    return render_template('edit_post.html', form=form, post=post)

@app.route('/delete_post/<int:post_id>', methods=['POST'])
@login_required
def delete_post(post_id):
    post = Post.query.get_or_404(post_id)
    
    # 检查当前用户是否是文章作者
    if post.author != current_user:
        flash('您没有权限删除这篇文章', 'error')
        return redirect(url_for('index'))
    
    # 保存文章标题用于显示消息
    post_title = post.title
    
    # 删除文章封面图片（如果存在）
    if post.cover_image:
        image_path = os.path.join('static', post.cover_image)
        if os.path.exists(image_path):
            try:
                os.remove(image_path)
            except:
                pass  # 忽略删除错误
    
    # 删除文章
    db.session.delete(post)
    db.session.commit()
    
    flash(f'文章 "{post_title}" 已成功删除', 'success')
    return redirect(url_for('profile'))

# 更新所有文章的评分信息
def update_all_post_ratings():
    posts = Post.query.all()
    for post in posts:
        ratings = Rating.query.filter_by(post_id=post.id).all()
        count = len(ratings)
        
        if count > 0:
            # 计算平均评分
            total = sum(r.value for r in ratings)
            post.rating_count = count
            post.average_rating = total / count
        else:
            # 没有评分
            post.rating_count = 0
            post.average_rating = 0.0
    
    db.session.commit()
    print("已更新所有文章的评分信息")

# 确保数据库表存在
def init_db(reset=False):
    """初始化数据库，可选择是否重置数据库"""
    print(f"{TermColor.BLUE}开始{'重置' if reset else '初始化'}数据库...{TermColor.ENDC}")
    
    if reset:
        # 重置数据库前确认表是否存在
        inspector = inspect(db.engine)
        existing_tables = inspector.get_table_names()
        
        # 如果有表存在，则删除所有表
        if existing_tables:
            print(f"{TermColor.YELLOW}删除现有数据库表...{TermColor.ENDC}")
            db.drop_all()
    
    # 创建所有表
    db.create_all()
    print(f"{TermColor.GREEN}已创建数据库表: {', '.join(inspect(db.engine).get_table_names())}{TermColor.ENDC}")

    # 检查是否已有用户，如无则创建默认管理员账户
    if not User.query.first():
        print(f"{TermColor.YELLOW}创建默认管理员账户...{TermColor.ENDC}")
        admin = User(username='admin')
        admin.set_password('admin123')
        db.session.add(admin)
        
        # 创建一些示例Tag
        tags = ["python", "flask", "web开发", "数据库", "前端", "后端", "JavaScript", "CSS", "HTML", "AI", "机器学习"]
        for tag_name in tags:
            tag = Tag(name=tag_name)
            db.session.add(tag)
        
        # 创建一些示例文章
        for i in range(1, 6):
            post = Post(
                title=f"示例文章 {i}",
                content=f"这是示例文章 {i} 的内容。\n\n这是另一个段落。",
                user_id=1
            )
            # 随机添加2-3个标签
            tag_count = random.randint(2, 3)
            tag_indices = random.sample(range(len(tags)), tag_count)
            for idx in tag_indices:
                post.tags.append(Tag.query.filter_by(name=tags[idx]).first())
            
            db.session.add(post)
            
            # 添加一些示例评论
            for j in range(1, 4):
                comment = Comment(
                    content=f"这是示例文章 {i} 的评论 {j}。",
                    user_id=1,
                    post_id=i
                )
                db.session.add(comment)
                
                # 为每个评论添加1-2个回复
                reply_count = random.randint(1, 2)
                for k in range(1, reply_count + 1):
                    reply = Comment(
                        content=f"这是对评论 {j} 的回复 {k}。",
                        user_id=1,
                        post_id=i,
                        parent_id=j
                    )
                    db.session.add(reply)
        
        # 添加默认统计数据
        stats_defaults = {
            'total_visits': '0',
            'unique_visitors': '0',
            'last_reset': datetime.now().strftime('%Y-%m-%d')
        }
        for key, value in stats_defaults.items():
            stat = Statistic(key=key, value=value)
            db.session.add(stat)
        
        # 提交所有变更
        db.session.commit()
        print(f"{TermColor.GREEN}已创建管理员账户和示例数据！{TermColor.ENDC}")
    else:
        print(f"{TermColor.GREEN}已有账户，跳过创建默认账户{TermColor.ENDC}")
    
    # 输出初始化完成的信息
    db_tables = inspect(db.engine).get_table_names()
    print(f"{TermColor.GREEN}数据库初始化完成。共有{len(db_tables)}个表：{', '.join(db_tables)}{TermColor.ENDC}")

@app.route('/static/<path:filename>')
def serve_static(filename):
    # 检查请求的文件是否在uploads目录下且不存在
    if filename.startswith('uploads/') and not os.path.isfile(os.path.join('static', filename)):
        # 返回默认图片
        return send_from_directory('static/img', 'default-cover.jpg')
    return send_from_directory('static', filename)

# 搜索功能
@app.route('/search')
def search():
    query = request.args.get('q', '')
    if not query:
        return redirect(url_for('index'))
    
    # 在标题和内容中搜索关键词
    search_terms = '%' + query + '%'
    posts = Post.query.filter(
        db.or_(
            Post.title.like(search_terms),
            Post.content.like(search_terms)
        )
    ).order_by(Post.created_at.desc()).all()
    
    # 为搜索结果处理纯文本摘要
    for post in posts:
        # 先将 markdown 转为 HTML
        html_content = markdown.markdown(post.content)
        # 将 HTML 转为纯文本（去除标签）
        text_content = bleach.clean(html_content, tags=[], strip=True)
        # 存储纯文本摘要
        post.text_excerpt = text_content[:150] + "..." if len(text_content) > 150 else text_content
    
    # 获取所有标签
    tags = Tag.query.all()
    
    # 更新在线用户数
    update_active_users()
    
    # 获取访问统计数据
    today_visits = visitor_stats.get('today_visits', 0)
    total_visits = visitor_stats.get('total_requests', 0)
    
    return render_template('index.html', posts=posts, search_query=query,
                          today_visits=today_visits, total_visits=total_visits, tags=tags)

# 添加评分功能
@app.route('/rate/<int:post_id>', methods=['POST'])
@login_required
def rate_post(post_id):
    # 获取评分值，确保在1-5之间
    try:
        rating_value = int(request.form.get('rating', 0))
        if rating_value < 1 or rating_value > 5:
            flash('评分必须在1至5之间', 'error')
            return redirect(url_for('post', post_id=post_id))
    except:
        flash('无效的评分', 'error')
        return redirect(url_for('post', post_id=post_id))
    
    # 获取文章
    post = Post.query.get_or_404(post_id)
    
    # 检查用户是否已经评过分
    existing_rating = Rating.query.filter_by(user_id=current_user.id, post_id=post_id).first()
    
    if existing_rating:
        # 更新现有评分
        old_value = existing_rating.value
        existing_rating.value = rating_value
        
        # 更新文章的平均评分
        if post.rating_count > 0:
            # 从总评分中减去旧评分，加上新评分
            total_rating = post.average_rating * post.rating_count
            total_rating = total_rating - old_value + rating_value
            post.average_rating = total_rating / post.rating_count
    else:
        # 创建新评分
        new_rating = Rating(user_id=current_user.id, post_id=post_id, value=rating_value)
        db.session.add(new_rating)
        
        # 更新文章的平均评分
        if post.rating_count == 0:
            post.average_rating = rating_value
            post.rating_count = 1  # 设置评分次数为1
        else:
            # 计算新的平均评分
            total_rating = post.average_rating * post.rating_count + rating_value
            post.rating_count += 1
            post.average_rating = total_rating / post.rating_count
            
    db.session.commit()
    flash('评分已提交，感谢您的反馈！', 'success')
    return redirect(url_for('post', post_id=post_id))

# 生成图片占位符
@app.route('/placeholder')
def placeholder():
    # 获取请求的宽度和高度
    width = request.args.get('width', 400, type=int)
    height = request.args.get('height', 225, type=int)
    
    # 返回默认封面图片
    return send_from_directory('static/img', 'default-cover.jpg')

def get_system_info():
    """获取系统信息"""
    info = {}
    info['系统'] = f"{platform.system()} {platform.version()}"
    info['Python版本'] = platform.python_version()
    info['CPU核心数'] = psutil.cpu_count(logical=True)
    info['CPU使用率'] = f"{psutil.cpu_percent()}%"
    info['内存总量'] = f"{round(psutil.virtual_memory().total / (1024*1024*1024), 2)} GB"
    info['内存使用率'] = f"{psutil.virtual_memory().percent}%"
    info['磁盘使用率'] = f"{psutil.disk_usage('/').percent}%"
    return info

def get_app_info():
    """获取应用信息"""
    info = {}
    # 获取数据库路径并检查文件大小
    db_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "instance", "blog.db")
    if os.path.exists(db_path):
        db_size = os.path.getsize(db_path) / (1024*1024)  # MB
        info['数据库大小'] = f"{round(db_size, 2)} MB"
    else:
        info['数据库大小'] = "未找到数据库文件"
        
    # 获取注册用户数量
    user_count = User.query.count()
    info['注册用户数'] = user_count
    
    # 获取文章数量
    post_count = Post.query.count()
    info['文章总数'] = post_count
    
    # 获取标签数量
    tag_count = Tag.query.count()
    info['标签总数'] = tag_count
    
    return info

# 添加请求处理器，记录用户访问信息
@app.before_request
def log_request_info():
    """记录每个请求的用户IP和访问信息，并更新会话活动"""
    # 更新会话活动状态（用于在线用户统计）
    if not request.path.startswith('/static') and not request.path.startswith('/api/online_users'):
        update_active_users()
        
    # 获取当前时间
    current_time = datetime.now()
    current_time_str = current_time.strftime('%Y-%m-%d %H:%M:%S')
    current_date = current_time.strftime('%Y-%m-%d')
    
    # 检查是否是新的一天，如果是则重置今日访问量
    if current_date != visitor_stats['today_date']:
        visitor_stats['today_visits'] = 0
        visitor_stats['today_date'] = current_date
        # 更新数据库中的今日日期
        Statistic.set_value('today_date', current_date)
        Statistic.set_value('today_visits', '0')
    
    # 获取客户端IP地址
    if request.headers.getlist("X-Forwarded-For"):
        # 如果使用代理，获取真实IP
        client_ip = request.headers.getlist("X-Forwarded-For")[0].split(',')[0].strip()
    elif request.headers.getlist("X-Real-IP"):
        # 如果使用Nginx等反向代理，获取真实IP
        client_ip = request.headers.getlist("X-Real-IP")[0]
    else:
        client_ip = request.remote_addr
    
    # 获取请求的路径和方法
    path = request.path
    method = request.method
    
    # 获取用户代理信息
    user_agent = request.headers.get('User-Agent', '')
    
    # 获取引荐页面
    referer = request.headers.get('Referer', '')
    
    # 获取当前登录用户
    username = current_user.username if current_user.is_authenticated else '游客'
    
    # 获取IP地理位置
    location = get_ip_location(client_ip, visitor_stats['geoip_reader'])
    
    # 定义不计入访问统计的路径
    excluded_paths = ['/static', '/admin/refresh_stats', '/admin/ip_visits', '/api/online_users']
    
    # 更新访问统计信息，排除后台管理相关API和静态资源
    if not any(path.startswith(excluded) for excluded in excluded_paths):
        visitor_stats['total_requests'] += 1
        visitor_stats['unique_ips'].add(client_ip)
        visitor_stats['path_counts'][path] += 1
        visitor_stats['today_visits'] += 1  # 增加今日访问量
        
        # 添加到最近访问者列表
        visitor_info = {
            'time': current_time_str,
            'ip': client_ip,
            'method': method,
            'path': path,
            'user_agent': user_agent,
            'username': username,
            'location': location
        }
        visitor_stats['recent_visitors'].append(visitor_info)
        
        # 每10次请求更新一次数据库中的统计数据
        if visitor_stats['total_requests'] % 10 == 0:
            update_stats_to_db()
    
    # 根据请求方法选择颜色
    method_color = {
        'GET': TermColor.GREEN,
        'POST': TermColor.BLUE,
        'PUT': TermColor.YELLOW,
        'DELETE': TermColor.RED
    }.get(method, TermColor.GREEN)
    
    # 跳过静态文件请求的日志
    if not path.startswith('/static') and not path.startswith('/api/online_users'):
        # 获取路径对应的中文注释
        path_comment = {
            '/': '访问主页',
            '/about': '访问关于页面',
            '/profile': '访问个人中心',
            '/login': '访问登录页面',
            '/register': '访问注册页面',
            '/write': '访问写文章页面',
            '/admin': '访问管理后台',
            '/admin/ip_visits': '查看IP访问记录',
            '/admin/refresh_stats': '刷新统计数据',
            '/placeholder': '获取占位图片',
            '/api/notes': '获取笔记列表',
            '/api/tasks': '获取待办事项',
            '/api/online_users': '获取在线用户数',
            '/security_center': '访问安全中心',
            '/change_password': '访问修改密码页面',
            '/password_reset': '访问密码重置页面',
            '/verify': '访问安全验证页面'
        }.get(path, '')
        
        # 如果是文章页面，添加文章标题
        if path.startswith('/post/'):
            try:
                post_id = int(path.split('/')[-1])
                post = Post.query.get(post_id)
                if post:
                    path_comment = f'查看文章《{post.title}》'
            except:
                path_comment = '查看文章'
        
        # 如果是编辑文章页面，添加文章标题
        if path.startswith('/edit_post/'):
            try:
                post_id = int(path.split('/')[-1])
                post = Post.query.get(post_id)
                if post:
                    path_comment = f'编辑文章《{post.title}》'
            except:
                path_comment = '编辑文章'
        
        # 输出访问信息到终端
        log_line = (f"{TermColor.BOLD}[{current_time_str}] {TermColor.BLUE}{client_ip}{TermColor.ENDC} - "
                   f"{method_color}{method}{TermColor.ENDC} {TermColor.YELLOW}{path}{TermColor.ENDC}")
        
        # 添加用户信息
        if username != '游客':
            log_line += f" - 用户: {TermColor.GREEN}{username}{TermColor.ENDC}"
            
        # 添加地理位置信息
        log_line += f" - {TermColor.HEADER}[{location}]{TermColor.ENDC}"
        
        # 标记不计入访问统计的请求
        if any(path.startswith(excluded) for excluded in excluded_paths):
            log_line += f" - {TermColor.YELLOW}[不计入统计]{TermColor.ENDC}"
            
        # 添加路径注释
        if path_comment:
            log_line += f" - {TermColor.CYAN}[{path_comment}]{TermColor.ENDC}"
            
        print(log_line)
        
        # 仅在verbose模式下显示详细信息
        if app.config.get('VERBOSE_LOGGING', False):
            print(f"  User-Agent: {user_agent}")
            if referer:
                print(f"  Referer: {referer}")
            
            # 如果是POST请求，显示表单数据（排除密码字段）
            if method == 'POST' and request.form:
                form_data = {k: v for k, v in request.form.items() 
                            if k.lower() not in ['password', 'password2']}
                print(f"  表单数据: {form_data}")

# 从数据库加载统计数据
def load_stats_from_db():
    """从数据库加载统计数据到内存"""
    with app.app_context():
        # 加载总访问量
        total_requests = Statistic.get_value('total_requests', '0')
        visitor_stats['total_requests'] = int(total_requests)
        
        # 加载今日访问量和日期
        today_date = Statistic.get_value('today_date', datetime.now().strftime('%Y-%m-%d'))
        today_visits = Statistic.get_value('today_visits', '0')
        visitor_stats['today_date'] = today_date
        visitor_stats['today_visits'] = int(today_visits)
        
        # 如果日期不匹配，重置今日访问量
        current_date = datetime.now().strftime('%Y-%m-%d')
        if today_date != current_date:
            visitor_stats['today_date'] = current_date
            visitor_stats['today_visits'] = 0
            Statistic.set_value('today_date', current_date)
            Statistic.set_value('today_visits', '0')
            
        # 加载路径计数
        path_counts_str = Statistic.get_value('path_counts', '{}')
        try:
            path_counts_dict = eval(path_counts_str)  # 将字符串转换为字典
            visitor_stats['path_counts'] = collections.Counter(path_counts_dict)
        except:
            visitor_stats['path_counts'] = collections.Counter()
            
        # 加载唯一IP集合
        unique_ips_str = Statistic.get_value('unique_ips', '[]')
        try:
            unique_ips_list = eval(unique_ips_str)  # 将字符串转换为列表
            visitor_stats['unique_ips'] = set(unique_ips_list)
        except:
            visitor_stats['unique_ips'] = set()
        
        print(f"从数据库加载统计数据: 总访问量 {visitor_stats['total_requests']}, 今日访问量 {visitor_stats['today_visits']}")

# 将统计数据更新到数据库
def update_stats_to_db():
    """将内存中的统计数据更新到数据库"""
    try:
        # 更新总访问量
        Statistic.set_value('total_requests', str(visitor_stats['total_requests']))
        
        # 更新今日访问量和日期
        Statistic.set_value('today_date', visitor_stats['today_date'])
        Statistic.set_value('today_visits', str(visitor_stats['today_visits']))
        
        # 更新路径计数
        path_counts_dict = dict(visitor_stats['path_counts'])
        Statistic.set_value('path_counts', str(path_counts_dict))
        
        # 更新唯一IP集合
        unique_ips_list = list(visitor_stats['unique_ips'])
        Statistic.set_value('unique_ips', str(unique_ips_list))
    except Exception as e:
        print(f"更新统计数据到数据库时出错: {e}")

# 确保在应用关闭前将统计数据保存到数据库
@app.teardown_appcontext
def save_stats_on_shutdown(exception=None):
    """应用上下文结束时保存统计数据"""
    update_stats_to_db()

# 后台管理相关路由
@app.route('/admin')
@login_required
def admin():
    # 检查是否通过安全验证
    if 'verified' not in session or 'verified_time' not in session:
        return redirect(url_for('verify'))
    
    # 检查验证是否有效（会话保持1小时有效）
    now = datetime.now()
    if (now - session['verified_time']).total_seconds() > 60 * 60:  # 1小时有效期
        return redirect(url_for('verify'))
    
    # 获取所有用户
    users = User.query.all()
    
    # 获取所有文章
    posts = Post.query.order_by(Post.created_at.desc()).all()
    
    # 获取系统资源使用情况
    cpu_percent = round(psutil.cpu_percent(interval=1), 1)
    memory = psutil.virtual_memory()
    memory_percent = round(memory.percent, 1)
    disk = psutil.disk_usage('/')
    disk_percent = round(disk.percent, 1)
    update_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    
    return render_template('admin.html', users=users, posts=posts,
                          cpu_percent=cpu_percent,
                          memory_percent=memory_percent,
                          disk_percent=disk_percent,
                          update_time=update_time)

@app.route('/admin/refresh_stats')
@login_required
def refresh_stats():
    """刷新系统资源使用情况的API"""
    # 获取系统资源使用情况
    cpu_percent = round(psutil.cpu_percent(interval=1), 1)
    memory = psutil.virtual_memory()
    memory_percent = round(memory.percent, 1)
    disk = psutil.disk_usage('/')
    disk_percent = round(disk.percent, 1)
    update_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    
    # 返回JSON格式的数据
    return jsonify({
        'cpu_percent': cpu_percent,
        'memory_percent': memory_percent,
        'disk_percent': disk_percent,
        'update_time': update_time
    })

@app.route('/admin/ip_visits')
@login_required
def get_ip_visits():
    """获取最近的IP访问信息"""
    # 检查是否通过安全验证
    if 'verified' not in session or 'verified_time' not in session:
        return jsonify({'error': '未通过安全验证'}), 403
    
    # 检查验证是否有效（会话保持1小时有效）
    now = datetime.now()
    if (now - session['verified_time']).total_seconds() > 60 * 60:  # 1小时有效期
        return jsonify({'error': '验证已过期'}), 403
    
    # 获取最近的访问者信息（最多20条）
    recent_visitors = list(visitor_stats['recent_visitors'])[-20:]
    
    # 整理访问信息，按IP分组统计
    ip_stats = {}
    for visitor in recent_visitors:
        ip = visitor['ip']
        if ip not in ip_stats:
            ip_stats[ip] = {
                'ip': ip,
                'location': visitor['location'],
                'count': 0,
                'last_visit': visitor['time'],
                'paths': [],
                'user_agent': visitor['user_agent'],
                'is_banned': ip in visitor_stats['banned_ips'],
                'unban_time': visitor_stats['banned_ips'][ip].strftime('%Y-%m-%d %H:%M:%S') if ip in visitor_stats['banned_ips'] else None
            }
        
        ip_stats[ip]['count'] += 1
        if visitor['path'] not in ip_stats[ip]['paths']:
            ip_stats[ip]['paths'].append(visitor['path'])
    
    # 转换为列表并按访问次数排序
    ip_list = list(ip_stats.values())
    ip_list.sort(key=lambda x: x['count'], reverse=True)
    
    return jsonify({
        'ip_visits': ip_list,
        'total_ips': len(ip_list),
        'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    })

@app.route('/admin/delete_user/<int:user_id>', methods=['POST'])
@login_required
def admin_delete_user(user_id):
    # 获取要删除的用户
    user = User.query.get_or_404(user_id)
    
    # 防止删除管理员账户
    if user.username == 'admin':
        flash('不能删除管理员账户', 'error')
        return redirect(url_for('admin'))
    
    # 保存用户名用于显示消息
    username = user.username
    
    # 删除该用户的所有文章的封面图片
    for post in user.posts:
        if post.cover_image:
            image_path = os.path.join('static', post.cover_image)
            if os.path.exists(image_path):
                try:
                    os.remove(image_path)
                except:
                    pass  # 忽略删除错误
    
    # 删除用户（级联删除该用户的所有文章和评分）
    db.session.delete(user)
    db.session.commit()
    
    flash(f'用户 "{username}" 已成功删除', 'success')
    return redirect(url_for('admin'))

@app.route('/admin/delete_post/<int:post_id>', methods=['POST'])
@login_required
def admin_delete_post(post_id):
    # 获取要删除的文章
    post = Post.query.get_or_404(post_id)
    
    # 保存文章标题用于显示消息
    post_title = post.title
    
    # 删除文章封面图片（如果存在）
    if post.cover_image:
        image_path = os.path.join('static', post.cover_image)
        if os.path.exists(image_path):
            try:
                os.remove(image_path)
            except:
                pass  # 忽略删除错误
    
    # 删除文章
    db.session.delete(post)
    db.session.commit()
    
    flash(f'文章 "{post_title}" 已成功删除', 'success')
    return redirect(url_for('admin'))

@app.route('/verify')
def verify():
    # 检查当前是否有有效的验证码
    now = datetime.now()
    if 'verify_code' not in session or 'verify_time' not in session or \
       (now - session['verify_time']).total_seconds() > 30 * 60:  # 30分钟有效期
        # 生成4位随机验证码
        verify_code = ''.join([str(random.randint(0, 9)) for _ in range(4)])
        # 保存到会话
        session['verify_code'] = verify_code
        session['verify_time'] = now
        # 输出到控制台
        print(f"\n{TermColor.BOLD}{TermColor.GREEN}新的安全验证码: {verify_code}, 有效期至: {(now + timedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')}{TermColor.ENDC}\n")
    
    return render_template('verify.html')

@app.route('/verify_code', methods=['POST'])
def verify_code():
    # 获取用户输入的验证码
    user_code = request.form.get('code1', '') + \
                request.form.get('code2', '') + \
                request.form.get('code3', '') + \
                request.form.get('code4', '')
    
    # 检查验证码是否有效
    now = datetime.now()
    if 'verify_code' not in session or 'verify_time' not in session:
        flash('验证会话已过期，请重新获取验证码', 'error')
        return redirect(url_for('verify'))
    
    # 检查验证码是否过期
    if (now - session['verify_time']).total_seconds() > 30 * 60:  # 30分钟有效期
        flash('验证码已过期，请重新获取验证码', 'error')
        return redirect(url_for('verify'))
    
    # 检查验证码是否正确
    if user_code != session['verify_code']:
        flash('验证码错误，请重新输入', 'error')
        return redirect(url_for('verify'))
    
    # 验证通过，记录验证状态
    session['verified'] = True
    session['verified_time'] = now
    
    # 重定向到管理页面
    return redirect(url_for('admin'))

@app.route('/show_verify_code')
def show_verify_code():
    """在控制台再次显示当前的验证码"""
    # 检查当前是否有有效的验证码
    now = datetime.now()
    if 'verify_code' not in session or 'verify_time' not in session:
        return "验证码不存在", 404
    
    # 检查验证码是否过期
    if (now - session['verify_time']).total_seconds() > 30 * 60:  # 30分钟有效期
        return "验证码已过期", 404
    
    # 获取剩余有效时间
    remaining_seconds = 30 * 60 - (now - session['verify_time']).total_seconds()
    remaining_minutes = int(remaining_seconds / 60)
    remaining_seconds = int(remaining_seconds % 60)
    
    # 在控制台显示当前验证码
    print(f"\n{TermColor.BOLD}{TermColor.GREEN}当前安全验证码: {session['verify_code']}, 剩余有效期: {remaining_minutes}分{remaining_seconds}秒{TermColor.ENDC}\n")
    
    return "", 204  # 返回无内容状态码

@app.route('/comment/<int:post_id>', methods=['POST'])
@login_required
def add_comment(post_id):
    # 获取文章
    post = Post.query.get_or_404(post_id)
    
    # 处理评论表单
    form = CommentForm()
    if form.validate_on_submit():
        # 创建新评论
        comment = Comment(
            content=form.content.data,
            user_id=current_user.id,
            post_id=post_id
        )
        db.session.add(comment)
        db.session.commit()
        flash('评论已发布！', 'success')
    
    return redirect(url_for('post', post_id=post_id))

@app.route('/reply/<int:post_id>/<int:comment_id>', methods=['POST'])
def reply(post_id, comment_id):
    """回复评论功能"""
    
    # 验证用户是否登录
    if not current_user.is_authenticated:
        flash('请先登录后再回复评论', 'error')
        return redirect(url_for('login'))
    
    # 获取父评论
    parent_comment = Comment.query.get_or_404(comment_id)
    
    # 获取回复内容
    content = request.form.get('content')
    if not content or len(content.strip()) == 0:
        flash('回复内容不能为空', 'error')
        return redirect(url_for('post', post_id=post_id))
    
    # 创建新回复（也是一个评论，但有parent_id）
    reply = Comment(
        content=content,
        user_id=current_user.id,
        post_id=post_id,
        parent_id=comment_id,
        created_at=datetime.utcnow()
    )
    
    # 保存到数据库
    db.session.add(reply)
    db.session.commit()
    
    flash('回复成功', 'success')
    return redirect(url_for('post', post_id=post_id))

@app.route('/get_replies/<int:comment_id>')
def get_replies(comment_id):
    """获取指定评论的回复"""
    try:
        # 获取URL参数
        start = request.args.get('start', default=0, type=int)
        count = request.args.get('count', default=5, type=int)
        
        # 查找父评论
        parent_comment = Comment.query.get_or_404(comment_id)
        
        # 获取该评论的所有回复
        replies = Comment.query.filter_by(parent_id=comment_id).order_by(Comment.created_at.asc()).offset(start).limit(count).all()
        
        # 转换为JSON格式
        replies_data = []
        for reply in replies:
            user = User.query.get(reply.user_id)
            replies_data.append({
                'id': reply.id,
                'content': reply.content,
                'created_at': reply.created_at.strftime('%Y-%m-%d %H:%M'),
                'username': user.username if user else '匿名用户'
            })
        
        # 获取总回复数
        total_replies = Comment.query.filter_by(parent_id=comment_id).count()
        
        return jsonify({
            'success': True,
            'replies': replies_data,
            'total': total_replies
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'获取回复失败: {str(e)}'
        }), 500

@app.route('/get_all_tags', methods=['GET'])
def get_all_tags():
    """获取所有标签API端点"""
    tags = Tag.query.all()
    tags_list = [{'id': tag.id, 'name': tag.name} for tag in tags]
    return jsonify(tags_list)

# 更新在线用户数
def update_active_users():
    """
    使用会话ID跟踪活跃用户，提供更准确的在线用户统计
    """
    current_time = time.time()
    
    # 生成唯一会话ID（如果不存在）
    if 'session_id' not in session:
        session['session_id'] = str(uuid.uuid4())
        visitor_stats['active_sessions'].add(session['session_id'])
        visitor_stats['last_activity'][session['session_id']] = current_time
    
    # 更新当前会话的最后活动时间
    visitor_stats['last_activity'][session['session_id']] = current_time
    
    # 清理超时的会话
    expired_sessions = []
    for session_id in visitor_stats['active_sessions']:
        last_active = visitor_stats['last_activity'].get(session_id, 0)
        if current_time - last_active > ACTIVE_SESSION_TIMEOUT * 60:  # 转换为秒
            expired_sessions.append(session_id)
    
    # 移除过期会话
    for session_id in expired_sessions:
        visitor_stats['active_sessions'].discard(session_id)
        if session_id in visitor_stats['last_activity']:
            del visitor_stats['last_activity'][session_id]
    
    # 更新在线用户计数
    visitor_stats['online_users'] = len(visitor_stats['active_sessions'])
    
    # 更新存储到会话中的计数，用于模板渲染
    session['active_users'] = visitor_stats['online_users']
    
    return visitor_stats['online_users']

@app.route('/api/online_users', methods=['GET'])
def get_online_users():
    """API端点，用于获取当前在线用户数"""
    return jsonify({'online_users': visitor_stats['online_users']})

@app.route('/security_center')
@login_required
def security_center():
    """安全中心页面，显示用户的登录历史记录"""
    # 获取当前用户的登录历史记录，按时间倒序排序
    login_history = LoginHistory.query.filter_by(user_id=current_user.id).order_by(LoginHistory.login_time.desc()).all()
    return render_template('security_center.html', login_history=login_history)

# 添加待办事项相关路由
@app.route('/add_task', methods=['POST'])
@login_required
def add_task():
    """添加新的待办事项"""
    title = request.form.get('title')
    description = request.form.get('description', '')
    deadline_str = request.form.get('deadline', '')
    priority = request.form.get('priority', 0)
    
    if not title:
        flash('任务标题不能为空', 'error')
        return redirect(url_for('profile'))
    
    # 处理截止日期
    deadline = None
    if deadline_str:
        try:
            deadline = datetime.strptime(deadline_str, '%Y-%m-%d')
        except ValueError:
            flash('截止日期格式无效', 'error')
            return redirect(url_for('profile'))
    
    # 创建新任务
    task = Task(
        title=title,
        description=description,
        deadline=deadline,
        priority=int(priority),
        user_id=current_user.id
    )
    
    try:
        db.session.add(task)
        db.session.commit()
        flash('任务添加成功', 'success')
    except Exception as e:
        db.session.rollback()
        flash(f'添加任务失败: {str(e)}', 'error')
    
    return redirect(url_for('profile'))

@app.route('/complete_task/<int:task_id>', methods=['POST'])
@login_required
def complete_task(task_id):
    """标记任务为已完成或未完成"""
    task = Task.query.get_or_404(task_id)
    
    # 检查是否是当前用户的任务
    if task.user_id != current_user.id:
        flash('您无权修改此任务', 'error')
        return redirect(url_for('profile'))
    
    # 切换任务状态
    task.completed = not task.completed
    
    try:
        db.session.commit()
        status = '已完成' if task.completed else '未完成'
        flash(f'任务已标记为{status}', 'success')
    except Exception as e:
        db.session.rollback()
        flash(f'更新任务状态失败: {str(e)}', 'error')
    
    return redirect(url_for('profile'))

@app.route('/delete_task/<int:task_id>', methods=['POST'])
@login_required
def delete_task(task_id):
    """删除任务"""
    task = Task.query.get_or_404(task_id)
    
    # 检查是否是当前用户的任务
    if task.user_id != current_user.id:
        flash('您无权删除此任务', 'error')
        return redirect(url_for('profile'))
    
    try:
        db.session.delete(task)
        db.session.commit()
        flash('任务已删除', 'success')
    except Exception as e:
        db.session.rollback()
        flash(f'删除任务失败: {str(e)}', 'error')
    
    return redirect(url_for('profile'))

# 添加获取待办事项的API路由
@app.route('/api/tasks', methods=['GET'])
@login_required
def get_tasks():
    # 获取用户的所有待办事项
    tasks = Task.query.filter_by(user_id=current_user.id).order_by(Task.priority.desc(), Task.created_at.desc()).all()
    
    # 将结果转为JSON格式
    result = []
    for task in tasks:
        deadline_str = task.deadline.strftime('%Y-%m-%d %H:%M') if task.deadline else None
        result.append({
            'id': task.id,
            'title': task.title,
            'description': task.description,
            'completed': task.completed,
            'priority': task.priority,
            'deadline': deadline_str,
            'created_at': task.created_at.strftime('%Y-%m-%d %H:%M')
        })
    
    return jsonify({
        'success': True,
        'tasks': result
    })

# 添加更新任务状态的API路由
@app.route('/api/tasks/<int:task_id>/toggle', methods=['POST'])
@login_required
def toggle_task_status(task_id):
    """API端点，用于切换任务的完成状态"""
    task = Task.query.get_or_404(task_id)
    
    # 检查是否是当前用户的任务
    if task.user_id != current_user.id:
        return jsonify({'success': False, 'message': '您无权修改此任务'}), 403
    
    # 切换任务状态
    task.completed = not task.completed
    
    try:
        db.session.commit()
        return jsonify({
            'success': True, 
            'completed': task.completed,
            'message': '任务状态已更新'
        })
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'message': f'更新任务状态失败: {str(e)}'}), 500

# 随手记操作接口
@app.route('/api/notes', methods=['GET'])
@login_required
def get_notes():
    # 获取用户的所有随手记
    notes = Note.query.filter_by(user_id=current_user.id).order_by(Note.created_at.desc()).all()
    
    # 将结果转为JSON格式
    result = []
    for note in notes:
        result.append({
            'id': note.id,
            'content': note.content,
            'location': note.location,
            'created_at': note.created_at.strftime('%Y-%m-%d %H:%M')
        })
    
    return jsonify({
        'success': True,
        'notes': result
    })

@app.route('/api/notes/add', methods=['POST'])
@login_required
def add_note():
    # 获取表单数据
    content = request.form.get('content')
    location = request.form.get('location', '')
    
    if not content:
        return jsonify({'success': False, 'message': '内容不能为空'})
    
    # 创建新随手记
    new_note = Note(
        content=content,
        location=location,
        user_id=current_user.id
    )
    
    # 保存到数据库
    db.session.add(new_note)
    db.session.commit()
    
    return jsonify({
        'success': True,
        'note': {
            'id': new_note.id,
            'content': new_note.content,
            'location': new_note.location,
            'created_at': new_note.created_at.strftime('%Y-%m-%d %H:%M')
        },
        'message': '记录已保存'
    })

@app.route('/api/notes/<int:note_id>/delete', methods=['POST'])
@login_required
def delete_note(note_id):
    # 查找指定的随手记
    note = Note.query.filter_by(id=note_id, user_id=current_user.id).first()
    
    if not note:
        return jsonify({'success': False, 'message': '随手记不存在或无权限删除'})
    
    # 从数据库中删除
    db.session.delete(note)
    db.session.commit()
    
    return jsonify({'success': True, 'message': '记录已删除'})

@app.route('/api/tasks/add', methods=['POST'])
@login_required
def add_task_api():
    # 获取表单数据
    title = request.form.get('title')
    description = request.form.get('description', '')
    deadline_str = request.form.get('deadline', '')
    priority = request.form.get('priority', 0)
    
    if not title:
        return jsonify({'success': False, 'message': '任务标题不能为空'})
    
    # 处理截止日期
    deadline = None
    if deadline_str:
        try:
            deadline = datetime.strptime(deadline_str, '%Y-%m-%d')
        except ValueError:
            return jsonify({'success': False, 'message': '截止日期格式无效'})
    
    # 创建新任务
    new_task = Task(
        title=title,
        description=description,
        deadline=deadline,
        priority=int(priority),
        user_id=current_user.id
    )
    
    try:
        db.session.add(new_task)
        db.session.commit()
        return jsonify({
            'success': True,
            'task': {
                'id': new_task.id,
                'title': new_task.title,
                'description': new_task.description,
                'created_at': new_task.created_at.strftime('%Y-%m-%d %H:%M'),
                'completed': new_task.completed,
                'deadline': new_task.deadline.strftime('%Y-%m-%d') if new_task.deadline else None,
                'priority': new_task.priority
            },
            'message': '任务已添加'
        })
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'message': f'添加任务失败: {str(e)}'})

# 添加API路由获取近三天的待办事项
@app.route('/api/tasks/recent', methods=['GET'])
@login_required
def get_recent_tasks():
    # 获取今天的日期（中国时区）
    today = datetime.now() + timedelta(hours=8)
    today = today.replace(hour=0, minute=0, second=0, microsecond=0)
    
    # 计算三天前的日期
    three_days_ago = today - timedelta(days=3)
    
    # 获取近三天的待办事项（不包括今天）
    tasks = Task.query.filter(
        Task.user_id == current_user.id,
        Task.created_at >= three_days_ago,
        Task.created_at < today
    ).order_by(Task.created_at.desc()).all()
    
    # 将结果转为JSON格式
    result = []
    for task in tasks:
        deadline_str = task.deadline.strftime('%Y-%m-%d %H:%M') if task.deadline else None
        result.append({
            'id': task.id,
            'title': task.title,
            'description': task.description,
            'completed': task.completed,
            'priority': task.priority,
            'deadline': deadline_str,
            'created_at': task.created_at.strftime('%Y-%m-%d %H:%M')
        })
    
    return jsonify({
        'success': True,
        'tasks': result
    })

# 添加API路由获取近三天的随手记
@app.route('/api/notes/recent', methods=['GET'])
@login_required
def get_recent_notes():
    # 获取今天的日期（中国时区）
    today = datetime.now() + timedelta(hours=8)
    today = today.replace(hour=0, minute=0, second=0, microsecond=0)
    
    # 计算三天前的日期
    three_days_ago = today - timedelta(days=3)
    
    # 获取近三天的随手记（不包括今天）
    notes = Note.query.filter(
        Note.user_id == current_user.id,
        Note.created_at >= three_days_ago,
        Note.created_at < today
    ).order_by(Note.created_at.desc()).all()
    
    # 将结果转为JSON格式
    result = []
    for note in notes:
        result.append({
            'id': note.id,
            'content': note.content,
            'location': note.location,
            'created_at': note.created_at.strftime('%Y-%m-%d %H:%M')
        })
    
    return jsonify({
        'success': True,
        'notes': result
    })

# 添加按日期获取待办事项的API路由
@app.route('/api/tasks/by_date', methods=['GET'])
@login_required
def get_tasks_by_date():
    # 获取查询参数中的日期
    date_str = request.args.get('date')
    
    if not date_str:
        return jsonify({'success': False, 'message': '未提供日期参数'}), 400
        
    try:
        # 解析日期参数
        selected_date = datetime.strptime(date_str, '%Y-%m-%d')
        
        # 设置日期范围（当天的开始到结束）
        date_start = selected_date.replace(hour=0, minute=0, second=0, microsecond=0)
        date_end = selected_date.replace(hour=23, minute=59, second=59, microsecond=999999)
        
        # 查询指定日期的待办事项
        tasks = Task.query.filter(
            Task.user_id == current_user.id,
            Task.created_at >= date_start,
            Task.created_at <= date_end
        ).order_by(Task.priority.desc(), Task.created_at.desc()).all()
        
        # 将结果转为JSON格式
        result = []
        for task in tasks:
            deadline_str = task.deadline.strftime('%Y-%m-%d %H:%M') if task.deadline else None
            result.append({
                'id': task.id,
                'title': task.title,
                'description': task.description,
                'completed': task.completed,
                'priority': task.priority,
                'deadline': deadline_str,
                'created_at': task.created_at.strftime('%Y-%m-%d %H:%M')
            })
        
        return jsonify({
            'success': True,
            'tasks': result,
            'date': date_str
        })
        
    except ValueError:
        return jsonify({'success': False, 'message': '日期格式无效，请使用YYYY-MM-DD格式'}), 400
    except Exception as e:
        return jsonify({'success': False, 'message': f'查询失败: {str(e)}'}), 500

# 添加按日期获取随手记的API路由
@app.route('/api/notes/by_date', methods=['GET'])
@login_required
def get_notes_by_date():
    # 获取查询参数中的日期
    date_str = request.args.get('date')
    
    if not date_str:
        return jsonify({'success': False, 'message': '未提供日期参数'}), 400
        
    try:
        # 解析日期参数
        selected_date = datetime.strptime(date_str, '%Y-%m-%d')
        
        # 设置日期范围（当天的开始到结束）
        date_start = selected_date.replace(hour=0, minute=0, second=0, microsecond=0)
        date_end = selected_date.replace(hour=23, minute=59, second=59, microsecond=999999)
        
        # 查询指定日期的随手记
        notes = Note.query.filter(
            Note.user_id == current_user.id,
            Note.created_at >= date_start,
            Note.created_at <= date_end
        ).order_by(Note.created_at.desc()).all()
        
        # 将结果转为JSON格式
        result = []
        for note in notes:
            result.append({
                'id': note.id,
                'content': note.content,
                'location': note.location,
                'created_at': note.created_at.strftime('%Y-%m-%d %H:%M')
            })
        
        return jsonify({
            'success': True,
            'notes': result,
            'date': date_str
        })
        
    except ValueError:
        return jsonify({'success': False, 'message': '日期格式无效，请使用YYYY-MM-DD格式'}), 400
    except Exception as e:
        return jsonify({'success': False, 'message': f'查询失败: {str(e)}'}), 500

@app.before_request
def check_banned_ip():
    """检查IP是否被封禁"""
    # 获取客户端IP地址
    if request.headers.getlist("X-Forwarded-For"):
        client_ip = request.headers.getlist("X-Forwarded-For")[0].split(',')[0].strip()
    elif request.headers.getlist("X-Real-IP"):
        client_ip = request.headers.getlist("X-Real-IP")[0]
    else:
        client_ip = request.remote_addr
    
    # 检查IP是否被封禁
    if client_ip in visitor_stats['banned_ips']:
        ban_time = visitor_stats['banned_ips'][client_ip]
        if datetime.now() < ban_time:
            # IP仍在封禁期内
            return jsonify({
                'error': '访问过于频繁，请稍后再试',
                'unban_time': ban_time.strftime('%Y-%m-%d %H:%M:%S')
            }), 429
        else:
            # 封禁已过期，移除封禁记录
            del visitor_stats['banned_ips'][client_ip]
    
    # 检查访问频率
    current_time = datetime.now()
    if client_ip not in visitor_stats['ip_access_times']:
        visitor_stats['ip_access_times'][client_ip] = []
    
    # 清理过期的访问记录（1分钟前的记录）
    visitor_stats['ip_access_times'][client_ip] = [
        t for t in visitor_stats['ip_access_times'][client_ip]
        if (current_time - t).total_seconds() < 60
    ]
    
    # 添加当前访问时间
    visitor_stats['ip_access_times'][client_ip].append(current_time)
    
    # 检查是否超过频率限制
    if len(visitor_stats['ip_access_times'][client_ip]) > MAX_REQUESTS_PER_MINUTE:
        # 封禁IP
        unban_time = current_time + timedelta(seconds=BAN_DURATION)
        visitor_stats['banned_ips'][client_ip] = unban_time
        return jsonify({
            'error': '访问过于频繁，IP已被暂时封禁',
            'unban_time': unban_time.strftime('%Y-%m-%d %H:%M:%S')
        }), 429

class StaticFileFilter(logging.Filter):
    """过滤掉静态文件请求的日志"""
    def filter(self, record):
        if hasattr(record, 'msg') and isinstance(record.msg, str):
            # 检查日志消息是否包含静态文件请求或API请求的特征
            return not any(pattern in record.msg for pattern in [
                '/static/', 
                'static',
                '/api/online_users',
                '/api/notes',
                '.js',
                '.css',
                '.png',
                '.jpg',
                '.jpeg',
                '.gif',
                '.ico'
            ])
        return True

# 配置日志过滤器
@app.before_first_request
def setup_logging():
    """配置日志过滤器和日志级别"""
    # 配置 Werkzeug 的日志记录器
    werkzeug_logger = logging.getLogger('werkzeug')
    werkzeug_logger.addFilter(StaticFileFilter())
    
    # 设置 Werkzeug 的日志级别为 WARNING，这样就不会显示普通的访问日志
    werkzeug_logger.setLevel(logging.WARNING)
    
    # 配置 Flask 的日志记录器
    flask_logger = app.logger
    flask_logger.addFilter(StaticFileFilter())

if __name__ == '__main__':
    # 解析命令行参数
    parser = argparse.ArgumentParser(description='云水禅心居博客服务器')
    parser.add_argument('-p', '--port', type=int, default=5000, help='指定服务器端口，默认为5000')
    parser.add_argument('-d', '--debug', action='store_true', help='启用调试模式')
    parser.add_argument('-v', '--verbose', action='store_true', help='启用详细日志记录')
    parser.add_argument('-s', '--stats', action='store_true', help='定期显示访问统计信息')
    args = parser.parse_args()
    
    # 设置应用配置
    host = '0.0.0.0'
    port = args.port
    debug = args.debug
    app.config['VERBOSE_LOGGING'] = args.verbose
    
    # 重置数据库（第一次运行设置为True，之后改回False）
    init_db(reset=False)
    
    # 显示服务器信息
    import socket
    import sys
    import threading
    import time

    # 定义后台保存统计数据的线程函数
    def background_save_stats():
        """后台定期保存统计数据到数据库"""
        while True:
            try:
                # 每5分钟保存一次统计数据
                time.sleep(300)
                update_stats_to_db()
                print(f"{TermColor.GREEN}已将访问统计数据保存到数据库{TermColor.ENDC}")
            except Exception as e:
                print(f"{TermColor.RED}保存统计数据时出错: {e}{TermColor.ENDC}")
    
    def get_ip_address():
        """获取本机IP地址"""
        try:
            # 创建一个临时socket连接获取本机IP
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("8.8.8.8", 80))
            ip = s.getsockname()[0]
            s.close()
            return ip
        except:
            return "127.0.0.1"
    
    # 定期显示访问统计信息的函数
    def print_visitor_stats():
        while True:
            try:
                time.sleep(60)  # 每60秒显示一次
                
                if not visitor_stats['total_requests']:
                    continue
                    
                print("\n" + "-"*70)
                print(f"{TermColor.BOLD}{TermColor.HEADER}访问统计信息 ({datetime.now().strftime('%Y-%m-%d %H:%M:%S')}){TermColor.ENDC}")
                print(f"- 总请求数: {TermColor.BOLD}{visitor_stats['total_requests']}{TermColor.ENDC}")
                print(f"- 独立IP数: {TermColor.BOLD}{len(visitor_stats['unique_ips'])}{TermColor.ENDC}")
                print(f"- 今日访问量: {TermColor.BOLD}{visitor_stats['today_visits']}{TermColor.ENDC}")
                
                # 显示访问最多的5个路径
                print(f"- 热门访问路径:")
                for path, count in visitor_stats['path_counts'].most_common(5):
                    print(f"  {TermColor.YELLOW}{path}{TermColor.ENDC}: {count}次")
                    
                # 显示最近5个访问者
                print(f"- 最近访问者:")
                recent = list(visitor_stats['recent_visitors'])[-5:]
                for visitor in recent:
                    print(f"  {TermColor.BOLD}[{visitor['time']}]{TermColor.ENDC} {TermColor.BLUE}{visitor['ip']}{TermColor.ENDC} "
                          f"({visitor['location']}) - {visitor['path']}")
                    
                print("-"*70)
                
                # 更新统计数据到数据库
                update_stats_to_db()
            except Exception as e:
                print(f"显示统计信息时出错: {e}")
    
    # 尝试加载GeoIP数据库
    def load_geoip_database():
        if not GEOIP_AVAILABLE:
            print(f"{TermColor.YELLOW}提示: geoip2库未安装，无法获取IP地理位置信息{TermColor.ENDC}")
            return None
            
        # 尝试从多个可能的位置加载数据库文件
        possible_paths = [
            './GeoLite2-City.mmdb',
            os.path.expanduser('~/GeoLite2-City.mmdb'),
            '/usr/share/GeoIP/GeoLite2-City.mmdb',
            '/var/lib/GeoIP/GeoLite2-City.mmdb',
            'C:\\GeoIP\\GeoLite2-City.mmdb',
        ]
        
        for path in possible_paths:
            if os.path.exists(path):
                try:
                    reader = geoip2.database.Reader(path)
                    print(f"{TermColor.GREEN}已成功加载GeoIP数据库: {path}{TermColor.ENDC}")
                    return reader
                except Exception as e:
                    print(f"{TermColor.RED}加载GeoIP数据库失败: {e}{TermColor.ENDC}")
                    
        print(f"{TermColor.YELLOW}提示: 未找到GeoIP数据库文件，无法获取IP地理位置信息{TermColor.ENDC}")
        print(f"{TermColor.YELLOW}可以从 https://dev.maxmind.com/geoip/geolite2-free-geolocation-data 下载免费数据库{TermColor.ENDC}")
        return None
    
    # 为Windows终端启用ANSI颜色支持
    if platform.system() == 'Windows':
        import ctypes
        kernel32 = ctypes.windll.kernel32
        kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
    
    # 加载GeoIP数据库
    visitor_stats['geoip_reader'] = load_geoip_database()
    
    print("\n" + "="*70)
    print(f"{TermColor.BOLD}{TermColor.HEADER}" + " "*25 + "云水禅心居博客服务器" + " "*25 + f"{TermColor.ENDC}")
    print("="*70)
    
    # 显示连接信息
    print(f"{TermColor.BOLD}▋ 连接信息:{TermColor.ENDC}")
    print(f"  {TermColor.BOLD}本地访问地址: {TermColor.GREEN}http://127.0.0.1:{port}{TermColor.ENDC}")
    print(f"  {TermColor.BOLD}局域网访问地址: {TermColor.GREEN}http://{get_ip_address()}:{port}{TermColor.ENDC}")
    print(f"  {TermColor.BOLD}服务器运行模式: {TermColor.YELLOW if debug else TermColor.BLUE}{'调试模式' if debug else '生产模式'}{TermColor.ENDC}")
    print(f"  {TermColor.BOLD}日志详细程度: {TermColor.YELLOW if args.verbose else TermColor.BLUE}{'详细' if args.verbose else '标准'}{TermColor.ENDC}")
    print(f"  {TermColor.BOLD}访问统计显示: {TermColor.GREEN if args.stats else TermColor.RED}{'开启' if args.stats else '关闭'}{TermColor.ENDC}")
    print(f"  {TermColor.BOLD}启动时间: {TermColor.BLUE}{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}{TermColor.ENDC}")
    
    # 显示系统信息
    print(f"\n{TermColor.BOLD}▋ 系统信息:{TermColor.ENDC}")
    try:
        sys_info = get_system_info()
        for key, value in sys_info.items():
            value_color = TermColor.BLUE
            # 为CPU和内存使用率设置颜色
            if "使用率" in key:
                try:
                    usage = float(value.strip('%'))
                    if usage > 80:
                        value_color = TermColor.RED
                    elif usage > 50:
                        value_color = TermColor.YELLOW
                except:
                    pass
            print(f"  {TermColor.BOLD}{key}: {value_color}{value}{TermColor.ENDC}")
    except Exception as e:
        print(f"  {TermColor.RED}无法获取系统信息: {e}{TermColor.ENDC}")
    
    # 显示应用信息
    print(f"\n{TermColor.BOLD}▋ 应用信息:{TermColor.ENDC}")
    try:
        app_info = get_app_info()
        for key, value in app_info.items():
            print(f"  {TermColor.BOLD}{key}: {TermColor.GREEN}{value}{TermColor.ENDC}")
    except Exception as e:
        print(f"  {TermColor.RED}无法获取应用信息: {e}{TermColor.ENDC}")
    
    print("\n" + "="*70)
    print(f"{TermColor.BOLD}{TermColor.YELLOW}" + " "*20 + "服务器正在运行中，按Ctrl+C停止服务" + " "*20 + f"{TermColor.ENDC}")
    print("="*70)
    print(f"{TermColor.BOLD}▋ 实时访问日志:{TermColor.ENDC}")
    
    # 如果开启统计显示，启动统计线程
    if args.stats:
        stats_thread = threading.Thread(target=print_visitor_stats, daemon=True)
        stats_thread.start()
    
    # 启动后台保存统计数据的线程
    save_thread = threading.Thread(target=background_save_stats, daemon=True)
    save_thread.start()
    
    # 运行应用
    app.run(host=host, port=port, debug=debug)